﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sinacor.OMM.MessageEntity;
using System.Threading;
using Sinacor.OMM.ConnectionManager.InternalQueue;
using Sinacor.OMM.Logging;
using System.Diagnostics;
using Sinacor.OMM.DumpClient;
using Sinacor.OMM.Util;
using System.IO;
using System.Reflection;
using PSE.Framework.Threading;

namespace Sinacor.OMM.Receptor.CommunicationChannel
{
    public class STMChannel : CommunicationChannelBase, ICommunicationChannel, IDisposable
    {
        #region Constantes

        const int TIME_TO_CHECK_CONNECTION = 20000;
        const int TIME_TO_CHECK_DAY_OPENED = 60000;
        const int TIME_TO_WAIT_FOR_CONNECTION_EVENT = 10000;
        const int TIME_TO_WAIT_FOR_DISCONNECT_STMCOM = 12000;
        const int TIME_TO_WAIT_FOR_TRYING_CONNECT = 5000;
        const int TIME_TO_RETRY_SEND_MESSAGE = 5000;
        const int TIME_TO_CHECK_INTERNALQUEUE = 25;
        const int LIMIT_TOTAL_INTERNALQUEUE = 8000;
        const long LIMIT_PRIVATE_MEMORY = 100000;
        const int LIMIT_HANDLE_COUNT = 2000;
        const int LIMIT_THREAD_COUNT = 200;
        const int TIME_UNLOAD_OBJECTS = 30000;
        const int LIMIT_UNSUCESSFULCONNECTIONRETRIES = 50;
            
        const string RECEPTOR_INTERNAL_QUEUE = "ReceptorInternalQueue";
        const string RECEPTOR_FOLDER_DUMP = "Receptor";
        const string RECEPTOR_TRACE_STM = "Receptor";
        const string RECEPTOR_CONTROLLER_FILENAME = "StmLastMsgId.ini";

        #endregion

        #region Atributos

        public delegate void HealthStatusChanged(STMChannel stmChannel);

        private ManualResetEvent _waitForConnect;
        //private System.Messaging.Message _stmMessage = new System.Messaging.Message();

        public delegate void CriticalErrorHandler();
        public event CriticalErrorHandler OnCriticalErrorEvent;

        private TraceWriter _traceWriterSTM;
        private TraceWriter _traceWriterReceivedMessages;
        private TraceWriter _traceWriterPutMessages;
        private TraceWriter _traceWriterHealthMonitor;

        private Thread _threadKeepUp;

        OMM.Receptor.Adapters.STMAdapter _stmAdapter;
        private InternalQueueManager<System.Messaging.Message> _queueReceivedMessage;

        private Action _onAcceptStartEventHandler;
        private Action _onConnectEventHandler;
        private Action<string> _onDisconnectEventHandler;
        private Sinacor.OMM.Receptor.Adapters.Delegates.OnErrorEventHandler _onErrorEventHandler;
        private Sinacor.OMM.Receptor.Adapters.Delegates.OnReceiveDataEventHandler _onReceiveDataEventHandler;
        private Action _onStartedEventHandler;

        private OMM.MessageEntity.OMMSystem.OMMSystemMessage _messageReceiveRestart;

        Thread thrReceiver;
        Thread thrMessagesDelivery;

        Dump _dumpContingency;
        DumpEntity.DumpLineData _dumpLineData;

        //private Sinacor.OMM.ConnectionManagerSTM.StmLastIdFileManager _stmLastIdFileManager;

        private string _lastMsgIdDelivered;

        private bool _keepRunning;
        private bool _connected;
        private bool _tryingConnect = true;
        private bool _dayResetKeepUp = false;

        private DateTime _dateSystem = DateTime.MinValue;
        private DateTime _lastHealthStatusChecked;
        private DateTime _lastMessageReceivedTime;
        private TimeSpan _avgSendAckTime;
        private TimeSpan _avgCallAckTime;
        private TimeSpan _limitAvgSendAckTime;
        private TimeSpan _limitAvgCallAckTime;
        private long _eventErrorReceivedQuantity;
        private long _sucessfulConnectionRetries;
        private long _unsucessfulConnectionRetries;
        private string _lastMsgIdReceived;
        private int _totalMessageReceivedPerInterval;
        private int _messagesReceivedPerSecond;
        private long _totalReceivedMessage;
        private long _sequencialReceivedMessage;
        private long _sequentialId;
        private long _receiverQueueConnectionsReTries;
        private long _dumpQueueConnectionsReTries;

        private string _readLastMsgId;
        private string[] _readLastMsgIds;

        Sinacor.OMM.HealthMonitor.Collector.HealthMonitorCollector _healthMonitor;
        Sinacor.OMM.HealthMonitor.Collector.SharedHealthDataMemoryStorage.Receiver.ReceiverShdms _receiverHealthStatus;
        #endregion

        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="communicationChannel"></param>
        public STMChannel(OMM.Configuration.Receptor.CommunicationChannel communicationChannel, OMM.Configuration.OrderBovespaSystem.OrderBovespaSystemConfiguration orderBovespaSystem)
            : base(communicationChannel)
        {
            this.OnCriticalErrorEvent += new CriticalErrorHandler(STMChannel_OnCriticalErrorEvent);

            _healthMonitor = Sinacor.OMM.HealthMonitor.Collector.HealthMonitorCollector.GetInstance();
            _receiverHealthStatus = Sinacor.OMM.HealthMonitor.Collector.SharedHealthDataMemoryStorage.Receiver.ReceiverShdms.GetSharedStorage();
            _healthMonitor.AddHealthRequestListener(Collect, Sinacor.OMM.HealthMonitor.Entity.HealthType.Receiver);

           
        }

        private void KeepUp()
        {
            _keepRunning = true;

            while (_keepRunning)
            {

                if (
                    (_unsucessfulConnectionRetries >= LIMIT_UNSUCESSFULCONNECTIONRETRIES || _dayResetKeepUp) &&
                    ReceptorConfigurationManager.OrderBovespaSystemInformation.State == Sinacor.OMM.Configuration.OrderBovespaSystem.SystemState.Opened
                    )
                {

                    if(!_dayResetKeepUp)
                        _traceWriterSTM.Trace("HUB STM Desconectado, Restartando o Serviço do Receptor.");
                    else
                        _traceWriterSTM.Trace("Data do Movimento Alterada, Restartando o Serviço do Receptor.");

                    ProcessStartInfo _process = new ProcessStartInfo();

                    String _receptorAssembly = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
                    _process.FileName = @_receptorAssembly + "/Sinacor.OMM.Receptor.KeepUp.exe";
                    _process.UseShellExecute = true;

                    if (File.Exists(_process.FileName))
                    {
                        System.Diagnostics.Process.Start(_process);
                    }
                    else
                    {
                        _traceWriterSTM.Trace("Não existe o arquivo de restart do Receptor: " + _process.FileName);
                    }

                    _dayResetKeepUp = false;
                }
                Sleeper.Sleep(TIME_TO_CHECK_CONNECTION, IsRunning);
            }
        }

        private object Collect()
        {
            CheckMessageQueueHealthStatus();

            //Message Processor
            _receiverHealthStatus.ReceiverDataInfo.StatisticInfo.lReceivedMessagesQuantity = TotalReceivedMessage;
            _receiverHealthStatus.ReceiverDataInfo.StatisticInfo.LastReceivedMessageTimeStamp = LastMessageReceivedTime;
            _receiverHealthStatus.ReceiverDataInfo.StatisticInfo.ReceivedMessagesForSecond = MessagesReceivedPerSecond;
            _receiverHealthStatus.ReceiverDataInfo.StatisticInfo.LastReceivedMessageId = _lastMsgIdReceived;
            _receiverHealthStatus.ReceiverDataInfo.StatisticInfo.TotalInternalQueuedMessages = LIMIT_TOTAL_INTERNALQUEUE;
            _receiverHealthStatus.ReceiverDataInfo.StatisticInfo.InternalQueuedMessages_01_Quantity = _queueReceivedMessage.Count(RECEPTOR_INTERNAL_QUEUE); 


            //STM
            _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.AverageTimeToSendAck = _avgSendAckTime.ToString();
            _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.LimitAverageTimeToSendAck = _limitAvgSendAckTime.ToString();
            _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.AverageTimeToCallAck = _avgCallAckTime.ToString();
            _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.LimitAverageTimeToCallAck = _limitAvgCallAckTime.ToString();
            _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.ErrorEventsQuantity = EventErrorReceivedQuantity;
            if (_stmAdapter != null)
            {
                _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.SuccessfulConnectionsQuantity = SucessfulConnectionRetries;
                _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.UnsuccessfulConnectionsQuantity = UnsucessfulConnectionRetries;
            }
            else
            {
                _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.SuccessfulConnectionsQuantity = 0;
                _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.UnsuccessfulConnectionsQuantity = 0;
            }


            //Process
            Process currentProcess = Process.GetCurrentProcess();

            _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.PrivateMemory = currentProcess.PrivateMemorySize64;
            _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.LimitPrivateMemory = LIMIT_PRIVATE_MEMORY;
            _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.HandleCount = currentProcess.HandleCount;
            _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.LimitHandleCount = LIMIT_HANDLE_COUNT;
            _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.ThreadCount = currentProcess.Threads.Count;
            _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.LimitThreadCount = LIMIT_THREAD_COUNT;
            _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.PrivilegedProcessorTime = currentProcess.PrivilegedProcessorTime.ToString();
            _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.InitializedDate = currentProcess.StartTime;

            _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.ThreadId = Thread.CurrentThread.ManagedThreadId.ToString();
            _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.ProcessId = currentProcess.Id.ToString();
            _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.MachineMacAddress = ReceptorConfigurationManager.ReceptorConfiguration.CommunicationChannels[0].MacAddress;

            //MSMQ
            _receiverHealthStatus.ReceiverDataInfo.MsmqConnectionInfo.ReceiverQueueConnectionsReTries = _receiverQueueConnectionsReTries;
            _receiverHealthStatus.ReceiverDataInfo.MsmqConnectionInfo.DumpQueueConnectionsReTries = _dumpQueueConnectionsReTries;

            //Machine
            if (String.IsNullOrEmpty(_receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.MachineName))
                _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.MachineName = System.Net.Dns.GetHostName();
            if (String.IsNullOrEmpty(_receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.MachineIPAddress))
                _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.MachineIPAddress = System.Net.Dns.GetHostEntry(System.Net.Dns.GetHostName()).AddressList[0].ToString();

            TracerHealthMonitor();

            return _receiverHealthStatus;
        }

        void STMChannel_OnCriticalErrorEvent()
        {
            try
            {
                EventLog.WriteEntry(LogSources.ReceptorService, "Critical error: Receptor Service was stopped." + System.Environment.NewLine
                    + "Possible Causes: STMCom.dll not found in Component Services", EventLogEntryType.Error);
                thrReceiver.Abort();


                _receiverHealthStatus.ReceiverDataInfo.ErrorData = new Sinacor.OMM.HealthMonitor.Entity.Common.ErrorData();
                _receiverHealthStatus.ReceiverDataInfo.ErrorData.ErrorDescription = "Critical error: Receptor Service was stopped.";
                _receiverHealthStatus.ReceiverDataInfo.ErrorData.ExceptionMessage = "Critical error: Receptor Service was stopped.";
                _receiverHealthStatus.ReceiverDataInfo.ErrorData.ProcedureToTake = "Possible Causes: STMCom.dll not found in Component Services";

                UnloadStm();
            }
            catch (Exception ex)
            {
                throw ex;
            }
            finally
            {
                System.Diagnostics.Process.GetCurrentProcess().Kill();
            }
        }

        void MainRepositoryMessage_OnHealthStatusChanged(Sinacor.OMM.ConnectionManager.MSMQ.MsmqSender MsmqSender)
        {
            //MSMQ
            _receiverHealthStatus.ReceiverDataInfo.MsmqConnectionInfo.ReceiverQueue.QueueAcessible = MsmqSender.IsConnected;
            _receiverHealthStatus.ReceiverDataInfo.MsmqConnectionInfo.ReceiverQueue.QueuedMessages = MsmqSender.QueuedMessages;
            _receiverHealthStatus.ReceiverDataInfo.MsmqConnectionInfo.ReceiverQueue.QueueBytesSize = MsmqSender.QueueSizeBytes;
            _receiverHealthStatus.ReceiverDataInfo.MsmqConnectionInfo.ReceiverQueue.MessagesPerSecond = MsmqSender.MessagesSentPerSecond;
            _receiverHealthStatus.ReceiverDataInfo.MsmqConnectionInfo.ReceiverQueue.QueuePath = MsmqSender.Path;
        }

        #endregion

        #region ICommunicationChannel Members

        /// <summary>
        /// Start
        /// </summary>
        public void Start()
        {
            try
            {
                //while (!System.Diagnostics.Debugger.IsAttached)
                //    System.Threading.Thread.Sleep(1000);

                if (OnCriticalErrorEvent == null)
                    throw new ArgumentNullException("Evento para tratamento de erro crítico tem que estar assinado");

                _traceWriterSTM = Tracer.GetInstance(RECEPTOR_TRACE_STM, "ReceptorSTMAdapter ");
                _traceWriterReceivedMessages = Tracer.GetInstance(RECEPTOR_TRACE_STM, "ReceptorSTMReceiverMessages ");
                _traceWriterPutMessages = Tracer.GetInstance(RECEPTOR_TRACE_STM, "ReceptorSTMPutMessages ");
                _traceWriterHealthMonitor = Tracer.GetInstance(RECEPTOR_TRACE_STM, "ReceptorSTMHealthMonitor ");

                ReceptorConfigurationManager.StartRefresh();

                _lastMsgIdDelivered = String.Empty;

                _queueReceivedMessage = InternalQueueManager<System.Messaging.Message>.GetInstance();

                _lastHealthStatusChecked = DateTime.Now;
                _waitForConnect = new ManualResetEvent(false);
                _limitAvgSendAckTime = new TimeSpan(0, 0, 0, 0, 5);
                _limitAvgCallAckTime = new TimeSpan(0, 0, 0, 0, 45);

                LoadStm();

                thrReceiver = new Thread(new ThreadStart(Run));
                thrReceiver.Name = "STM Receiver";
                thrReceiver.Start();

                _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.InitializedDate = DateTime.Now;

                _threadKeepUp = new Thread(new ThreadStart(KeepUp));
                _threadKeepUp.Name = "STM KeepUp";
                _threadKeepUp.Start();
            }
            catch (Exception ex)
            {
                EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);

                _receiverHealthStatus.ReceiverDataInfo.ErrorData = new Sinacor.OMM.HealthMonitor.Entity.Common.ErrorData();
                _receiverHealthStatus.ReceiverDataInfo.ErrorData.ErrorDescription = "Receptor Service not started.";
                _receiverHealthStatus.ReceiverDataInfo.ErrorData.ExceptionMessage = ex.Message;
                _receiverHealthStatus.ReceiverDataInfo.ErrorData.ProcedureToTake = "Possible Causes: Verify the configurations and restart proccess.";

                Stop();
            }
        }

        /// <summary>
        /// Stop
        /// </summary>
        public void Stop()
        {
            Dispose();
        }

        #endregion

        #region Threads Receive and Delivery

        private void Run()
        {
            //while (!System.Diagnostics.Debugger.IsAttached)
            //    System.Threading.Thread.Sleep(1000);

            _keepRunning = true;

            while (_keepRunning)
            {
                try
                {
                    if (_dateSystem != ReceptorConfigurationManager.OrderBovespaSystemInformation.SystemDate)
                    {
                        if (_dateSystem != DateTime.MinValue)
                            _dayResetKeepUp = true;

                        DayReset();
                        _dateSystem = new DateTime(ReceptorConfigurationManager.OrderBovespaSystemInformation.SystemDate.Year, ReceptorConfigurationManager.OrderBovespaSystemInformation.SystemDate.Month, ReceptorConfigurationManager.OrderBovespaSystemInformation.SystemDate.Day);
                    }

                    if (ReceptorConfigurationManager.OrderBovespaSystemInformation.State == Sinacor.OMM.Configuration.OrderBovespaSystem.SystemState.Opened)
                    {
                        _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.GoodStateConnected = true;
                        if (!_connected)
                        {
                            _waitForConnect.Reset();

                            if (_stmAdapter != null)
                            {
                                
                                _stmAdapter.User = base.CommunicationChannelConfiguration.User;
                                _stmAdapter.Employee = base.CommunicationChannelConfiguration.Employee;
                                _stmAdapter.Password = base.CommunicationChannelConfiguration.Password;
                                _stmAdapter.Ack = false;
                                _stmAdapter.Compressed = true;

                                _traceWriterSTM.Trace("Tentando Conectar com o STMClient[User:" + _stmAdapter.User + " Employee: " + _stmAdapter.Employee + "]");

                                if (_stmAdapter.Connect())
                                {
                                    if (_waitForConnect.WaitOne(TIME_TO_WAIT_FOR_CONNECTION_EVENT))
                                    {
                                        _connected = true;
                                    }
                                }

                                if (!_connected)
                                {
                                    _unsucessfulConnectionRetries++;
                                    _traceWriterSTM.Trace("Tentando Conectar com o STMClient[tentativas de conexão:" + _unsucessfulConnectionRetries + "]");
                                    UnloadStm();
                                }
                                else
                                {
                                    _sucessfulConnectionRetries++;
                                }
                            }
                            else
                            {
                                LoadStm();
                            }
                        }
                        else
                        {
                            //Apenas Aguarda o tempo para checar novamente se a conexão está OK
                            Sleeper.Sleep(TIME_TO_CHECK_CONNECTION, IsRunning);
                        }
                    }
                    else
                    {
                        _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.GoodStateConnected = false;

                        //if (_stmLastIdFileManager != null)
                        //{
                        //    _stmLastIdFileManager.Delete();
                        //}

                        if (_connected || _stmAdapter != null)
                        {                            
                            EventLog.WriteEntry(LogSources.ReceptorService, "OrderBovespaSystemInformation.State not opened!", EventLogEntryType.Information);                            
                            UnloadStm();
                        }

                        //Apenas Aguarda o tempo para checar novamente se a se o movimento está aberto
                        Sleeper.Sleep(TIME_TO_CHECK_DAY_OPENED, IsRunning);
                    }
                }
                catch (Exception ex)
                {
                    Sleeper.Sleep(TIME_TO_CHECK_CONNECTION, IsRunning);
                    _traceWriterSTM.Trace("Erro Run() desconhecido: " + ex.StackTrace);                    
                    EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
                    UnloadStm();
                }
            }
        }

        private void WaitForTryingConnectionAsync()
        {
            _tryingConnect = true;
            Thread thr = new Thread(new ThreadStart(WaitForTryingConnection));
            thr.Name = "Waiting Trying Connect";
            thr.Start();
        }

        private void WaitForTryingConnection()
        {
            Sleeper.Sleep(TIME_TO_WAIT_FOR_TRYING_CONNECT, IsTryingConnect);
            if (_tryingConnect)
            {
                try
                {
                    OnCriticalErrorEvent();
                }
                catch (Exception ex)
                {
                    EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
                }
            }
        }

        private bool IsTryingConnect()
        {
            return _tryingConnect;
        }

        private void MessageDelivery()
        {
            System.Messaging.Message stmMessage;

            _keepRunning = true;

            while (_keepRunning)
            {
                try
                {
                    while (_queueReceivedMessage.Count(RECEPTOR_INTERNAL_QUEUE) > 0)
                    {

                        lock (_queueReceivedMessage)
                            stmMessage = _queueReceivedMessage.Peek(RECEPTOR_INTERNAL_QUEUE);

                        if (putMessagesRepository(stmMessage))
                            _queueReceivedMessage.Dequeue(RECEPTOR_INTERNAL_QUEUE);

                    }

                    Sleeper.Sleep(TIME_TO_CHECK_INTERNALQUEUE, IsRunning);
                }
                catch (Exception ex)
                {
                    _traceWriterPutMessages.Trace("Erro na rotina de postar mensagens: " + ex.StackTrace);                    
                    EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
                }
            }
        }
        #endregion

        #region Funções usadas internamente

        private void LoadStm()
        {
            try
            {
                _traceWriterSTM.Trace("LoadSTM: Iniciando Objeto stmAdapter");

                _stmAdapter = new Sinacor.OMM.Receptor.Adapters.STMAdapter();

                _onAcceptStartEventHandler = new Action(_stmAdapter_OnAcceptStart);
                _stmAdapter.OnAcceptStart += _onAcceptStartEventHandler;

                _onConnectEventHandler = new Action(_stmAdapter_OnConnect);
                _stmAdapter.OnConnect += _onConnectEventHandler;

                _onDisconnectEventHandler = new Action<string>(_stmAdapter_OnDisconnect);
                _stmAdapter.OnDisconnect += _onDisconnectEventHandler;

                _onErrorEventHandler = new Sinacor.OMM.Receptor.Adapters.Delegates.OnErrorEventHandler(_stmAdapter_OnError);
                _stmAdapter.OnError += _onErrorEventHandler;

                _onReceiveDataEventHandler = new Sinacor.OMM.Receptor.Adapters.Delegates.OnReceiveDataEventHandler(_stmAdapter_OnReceiveData);
                _stmAdapter.OnReceiveData += _onReceiveDataEventHandler;

                _onStartedEventHandler = new Action(_stmAdapter_OnStarted);
                _stmAdapter.OnStarted += _onStartedEventHandler;

                _traceWriterSTM.Trace("UnloadSTM: Eventos Inicializados");

                //if (_stmLastIdFileManager != null && String.IsNullOrEmpty(_lastMsgIdDelivered))
                //{
                //    try
                //    {
                //        _readLastMsgId = _stmLastIdFileManager.ReadLastMsgId();
                //        if (!String.IsNullOrEmpty(_readLastMsgId))
                //        {
                //            _traceWriterSTM.Trace("LoadSTM: Objeto stmAdapter capturando Dados do Arquivo: [ String: " + _readLastMsgId + " ]");
                //            _readLastMsgIds = _readLastMsgId.Split('|');
                //            if (!String.IsNullOrEmpty(_readLastMsgIds[0])) _sequencialReceivedMessage = Convert.ToInt64(_readLastMsgIds[0].ToString());
                //            if (!String.IsNullOrEmpty(_readLastMsgIds[1])) _lastMsgIdDelivered = _readLastMsgIds[1];
                //            _traceWriterSTM.Trace("LoadSTM: Objeto stmAdapter Inicializando dados arquivo de Recovery: [ LastSequencialId: " + _sequencialReceivedMessage.ToString() + " LastMsgIddelivered: " + _lastMsgIdDelivered + " ]");
                //        }
                //    }
                //    catch (Exception ex)
                //    {
                //        _traceWriterSTM.Trace("Erro ReadLastMsgId File: " + ex.StackTrace);
                //    }
                //}

                _traceWriterSTM.Trace("LoadSTM: Objeto stmAdapter Inicializado.");
            }
            catch (Exception ex)
            {
                _traceWriterSTM.Trace("Erro LoadSTM: " + ex.StackTrace);
                EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
            }

        }

        private void UnloadStm()
        {
            try
            {
                _traceWriterSTM.Trace("UnloadSTM: Finalizando Objeto stmAdapter");

                if (_stmAdapter != null)
                {
                    _stmAdapter.Disconnect();

                    _stmAdapter.OnAcceptStart -= _onAcceptStartEventHandler;
                    _stmAdapter.OnConnect -= _onConnectEventHandler;
                    _stmAdapter.OnError -= _onErrorEventHandler;
                    _stmAdapter.OnReceiveData -= _onReceiveDataEventHandler;
                    _stmAdapter.OnStarted -= _onStartedEventHandler;

                    _traceWriterSTM.Trace("UnloadSTM: Eventos Finalizados");

                    _stmAdapter.Dispose();
                }

                _connected = false;

                _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.Connected = _connected;

                _onAcceptStartEventHandler = null;
                _onConnectEventHandler = null;
                _onErrorEventHandler = null;
                _onReceiveDataEventHandler = null;
                _onStartedEventHandler = null;
                _dumpContingency = null;
                _stmAdapter = null;
                //_stmLastIdFileManager = null;

                Sleeper.Sleep(TIME_UNLOAD_OBJECTS, IsRunning);

                _traceWriterSTM.Trace("UnloadSTM: Objeto stmAdapter Finalizado");
            }
            catch (Exception ex)
            {
                _onAcceptStartEventHandler = null;
                _onConnectEventHandler = null;
                _onErrorEventHandler = null;
                _onReceiveDataEventHandler = null;
                _onStartedEventHandler = null;
                _dumpContingency = null;
                _stmAdapter = null;

                _traceWriterSTM.Trace("Erro UnloadSTM: " + ex.StackTrace);
                EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);

            }
        }

        private void CheckMessageQueueHealthStatus()
        {
            _messagesReceivedPerSecond = CalculaNumeroMsgsPorSegundo();
        }

        private int CalculaNumeroMsgsPorSegundo()
        {
            int seconds;

            seconds = DateTime.Now.Subtract(_lastHealthStatusChecked).Seconds;

            if (seconds == 0)
                seconds = TIME_TO_CHECK_CONNECTION / 1000;

            if (_totalMessageReceivedPerInterval >= seconds)
                _messagesReceivedPerSecond = _totalMessageReceivedPerInterval / seconds;
            else
                _messagesReceivedPerSecond = _totalMessageReceivedPerInterval;

            _totalMessageReceivedPerInterval = 0;
            _lastHealthStatusChecked = DateTime.Now;

            return _messagesReceivedPerSecond;
        }

        private bool putMessagesRepository(System.Messaging.Message message)
        {
            bool _processOK = false;

            try
            {
                // Posta a mensagem na fila normal
                while (!_processOK && _keepRunning)
                {
                    _processOK = base.MainRepositoryMessage.Send(message);

                    if (_processOK)
                        _traceWriterPutMessages.Trace("Conseguiu enviar para a fila do roteador [" + message.Label + "]", TraceType.Optional);
                    else
                    {
                        _receiverQueueConnectionsReTries++;
                        _traceWriterPutMessages.Trace("ERRO: Não conseguiu para o roteador [" + message.Label + "]", TraceType.Optional);
                        _traceWriterPutMessages.Trace("Aguardará " + TIME_TO_RETRY_SEND_MESSAGE + "ms para tentar novamente.", TraceType.Optional);
                        Sleeper.Sleep(TIME_TO_RETRY_SEND_MESSAGE, () => _keepRunning);
                    }

                }

                // Posta a mensagem para o DUMPER
                _dumpLineData = new DumpEntity.DumpLineData();
                _dumpLineData.FileName = "DUMP" +
                                         ReceptorConfigurationManager.OrderBovespaSystemInformation.SystemDate.Year.ToString().PadLeft(4, '0') +
                                         ReceptorConfigurationManager.OrderBovespaSystemInformation.SystemDate.Month.ToString().PadLeft(2, '0') +
                                         ReceptorConfigurationManager.OrderBovespaSystemInformation.SystemDate.Day.ToString().PadLeft(2, '0');
                _dumpLineData.FolderName = RECEPTOR_FOLDER_DUMP;
                _dumpLineData.DataToDump = message.Body.ToString();

                _processOK = false;
                while (!_processOK && _keepRunning)
                {
                    _processOK = _dumpContingency.Send(_dumpLineData);

                    if (_processOK)
                        _traceWriterPutMessages.Trace("Conseguiu enviar a mensagem para o Dumper [" + message.Label + "]", TraceType.Optional);
                    else
                    {
                        _dumpQueueConnectionsReTries++;
                        _traceWriterPutMessages.Trace("ERRO: Não conseguiu enviar para o Dumper [" + message.Label + "]", TraceType.Optional);
                        _traceWriterPutMessages.Trace("Aguardará " + TIME_TO_RETRY_SEND_MESSAGE + "ms para tentar novamente.", TraceType.Optional);
                        Sleeper.Sleep(TIME_TO_RETRY_SEND_MESSAGE, () => _keepRunning);
                    }
                }

                // Escreve número da última mensagem recebida no arquivo
                _processOK = false;
                while (!_processOK && _keepRunning)
                {
                    try
                    {
                        //_stmLastIdFileManager.WriteLastMsgId(message.Label);
                        _processOK = true;

                        _traceWriterPutMessages.Trace("Conseguiu escrever MSGID em disco [" + message.Label + "]", TraceType.Optional);
                    }
                    catch (Exception ex)
                    {
                        _traceWriterPutMessages.Trace("ERRO: Não conseguiu escrever MSGID em disco [" + message.Label + "]", TraceType.Optional);
                        _traceWriterPutMessages.Trace("Aguardará " + TIME_TO_RETRY_SEND_MESSAGE + "ms para tentar novamente.", TraceType.Optional);
                        Sleeper.Sleep(TIME_TO_RETRY_SEND_MESSAGE, () => _keepRunning);
                    }
                }

                _traceWriterPutMessages.Trace("SUCESSO ao distribuir mensagem [" + message.Label + "] para roteador, dumper e escrever em disco!", TraceType.Optional);
                _processOK = true;
            }
            catch (Exception ex)
            {
                _processOK = false;

                _traceWriterPutMessages.Trace("Erro ao tentar postar as mensagens pro Roteador, Dumper ou escreve número da mensagem em disco!");
                _traceWriterPutMessages.Trace(ex.Message);
                _traceWriterPutMessages.Trace(ex.StackTrace);
                EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
            }


            return _processOK;
        }

        private bool IsConnected()
        {
            return _keepRunning && _connected;
        }

        private bool IsRunning()
        {
            return _keepRunning;
        }

        public void Dispose()
        {
            try
            {
                _keepRunning = false;

                ReceptorConfigurationManager.StopRefresh();

                if (_stmAdapter != null)
                    _stmAdapter.Dispose();

                //if (_stmLastIdFileManager != null)
                //    _stmLastIdFileManager.Dispose();

                if (_dumpContingency != null)
                    _dumpContingency.Dispose();

                if (_queueReceivedMessage != null)
                    _queueReceivedMessage.Dispose();

                if (_healthMonitor != null)
                    _healthMonitor.Dispose();

                if (_receiverHealthStatus != null)
                    _receiverHealthStatus.Dispose();

                if (base.MainRepositoryMessage != null)
                    base.MainRepositoryMessage.Dispose();

                if (base.MainRepositoryReceiver != null)
                    base.MainRepositoryReceiver.Dispose();

                UnloadStm();

                thrMessagesDelivery = null;
                thrReceiver = null;

                Tracer.Destroy();
            }
            catch (Exception ex)
            {
                _traceWriterSTM.Trace("Erro Dispose: " + ex.StackTrace);
                EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
            }
        }

        #endregion

        #region Métodos e propriedades usadas externamente

        public void DayReset()
        {
            _eventErrorReceivedQuantity = 0;
            _sucessfulConnectionRetries = 0;
            _unsucessfulConnectionRetries = 0;
            _totalReceivedMessage = 0;
            _sequencialReceivedMessage = 0;
            _lastMsgIdDelivered = string.Empty;
        }

        public void RetransmissionRequest(long sequencialIdRestart, string lastMsgId)
        {
            try
            {
                _traceWriterSTM.Trace("Solicitação de Retransmissão: [ SequencialId: " + sequencialIdRestart + " MsgId: " + lastMsgId + " ]");
                UnloadStm();

                _lastMsgIdDelivered = lastMsgId;

                //if (_queueReceivedMessage != null)
                //    _queueReceivedMessage.Clear(RECEPTOR_INTERNAL_QUEUE);

                //if (base.MainRepositoryMessage != null)
                //    base.MainRepositoryMessage.Purge();

                _sequencialReceivedMessage = sequencialIdRestart;
            }
            catch (Exception ex)
            {
                _traceWriterSTM.Trace("Erro RetransmissionRequest: " + ex.StackTrace);
                EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
            }
        }

        public long EventErrorReceivedQuantity
        {
            get { return _eventErrorReceivedQuantity; }
        }

        public long UnsucessfulConnectionRetries
        {
            get { return _unsucessfulConnectionRetries; }
        }

        public long SucessfulConnectionRetries
        {
            get { return _sucessfulConnectionRetries; }
        }

        public bool STMConnected
        {
            get { return _connected; }
        }

        public DateTime LastMessageReceivedTime
        {
            get { return _lastMessageReceivedTime; }
        }

        public long TotalReceivedMessage
        {
            get { return _totalReceivedMessage; }
        }

        public int MessagesReceivedPerSecond
        {
            get { return _messagesReceivedPerSecond; }
        }

        #endregion

        #region Eventos STM

        void _stmAdapter_OnStarted()
        {
            //
        }

        void _stmAdapter_OnReceiveData(string LastMsgId, string SPF_Header, string DataPtr, int DataSize)
        {
            if (_connected && _keepRunning)
            {
                try
                {
                    Stopwatch _stopWatchAvgAck = new Stopwatch();
                    _stopWatchAvgAck.Start();

                    System.Messaging.Message _stmMessage = new System.Messaging.Message();

                    _sequentialId = _sequencialReceivedMessage + 1;
                    _stmMessage.Label = _sequentialId.ToString() + "|" + LastMsgId;
                    _stmMessage.Body = DataPtr;

                    lock (_queueReceivedMessage)
                        _queueReceivedMessage.Enqueue(RECEPTOR_INTERNAL_QUEUE, _stmMessage);

                    _stopWatchAvgAck.Stop();
                    _avgSendAckTime = _stopWatchAvgAck.Elapsed;

                    Stopwatch _stopWatchAvgCallAck = new Stopwatch();
                    _stopWatchAvgCallAck.Start();

                    _stmAdapter.Ack = true;

                    _stopWatchAvgCallAck.Stop();
                    _avgCallAckTime = _stopWatchAvgCallAck.Elapsed;

                    _totalReceivedMessage++;
                    _sequencialReceivedMessage++;
                    _totalMessageReceivedPerInterval++;
                    _lastMessageReceivedTime = DateTime.Now;
                    _lastMsgIdReceived = LastMsgId;

                    _traceWriterReceivedMessages.Trace("Mensagem recebida e postada na fila Interna [ FilaInterna: " + RECEPTOR_INTERNAL_QUEUE + " NrSequencial: " + _sequencialReceivedMessage.ToString() + " MsgId: " + LastMsgId + " Message: " + DataPtr + " ]", TraceType.Optional);
                }
                catch (Exception ex)
                {
                    _traceWriterReceivedMessages.Trace("Erro ao receber mensagem e postar na fila interna (" + RECEPTOR_INTERNAL_QUEUE + "): " + ex.StackTrace);

                    EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);

                    UnloadStm();
                }
            }
        }

        void _stmAdapter_OnError(int Error, string Msg, string Desc)
        {
            _traceWriterSTM.Trace("Erro STMCom.DLL: [ CodErro: " + Error.ToString() + " Msg: " + Msg + " Desc: " + Desc + " ]");

            if (_connected && _keepRunning)
            {
                UnloadStm();
                
                EventLog.WriteEntry(LogSources.ReceptorService, Error.ToString() + " : " + Msg + " " + Desc, EventLogEntryType.Error);
            }
            _eventErrorReceivedQuantity++;
        }

        void _stmAdapter_OnDisconnect(string obj)
        {
            try
            {
                _traceWriterSTM.Trace("STMAdapter Disconectado");
                _connected = false;
                _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.Connected = _connected;

                MainRepositoryMessage.OnHealthStatusChanged -= new Sinacor.OMM.ConnectionManager.MSMQ.MsmqSender.HealthStatusChanged(MainRepositoryMessage_OnHealthStatusChanged);

                UnloadStm();

                //if (_queueReceivedMessage != null)
                //    _queueReceivedMessage.Clear(RECEPTOR_INTERNAL_QUEUE);

                //if (base.MainRepositoryMessage != null)
                //    base.MainRepositoryMessage.Purge();
            }
            catch (Exception ex)
            {
                _traceWriterSTM.Trace("Erro _stmAdapter_OnDisconnect: " + ex.StackTrace);
                EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
                UnloadStm();
            }
        }

        void _stmAdapter_OnConnect()
        {
            try
            {
                _traceWriterSTM.Trace("STMClient Conectado");

                _waitForConnect.Set();

                _connected = true;
                _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.Connected = _connected;

                _dumpQueueConnectionsReTries = 0;
                _receiverQueueConnectionsReTries = 0;

                if (_dumpContingency == null)
                    _dumpContingency = Dump.GetInstance();

                //if (_stmLastIdFileManager == null)
                //    _stmLastIdFileManager = new Sinacor.OMM.ConnectionManagerSTM.StmLastIdFileManager(Path.Combine(base.CommunicationChannelConfiguration.ControllerFilePath, RECEPTOR_CONTROLLER_FILENAME));

                if (String.IsNullOrEmpty(_lastMsgIdDelivered))
                {
                    _totalReceivedMessage = 0;

                    _lastMsgIdDelivered = ReceptorConfigurationManager.OrderBovespaSystemInformation.SystemDate.Year.ToString().PadLeft(4, '0') +
                                          ReceptorConfigurationManager.OrderBovespaSystemInformation.SystemDate.Month.ToString().PadLeft(2, '0') +
                                          ReceptorConfigurationManager.OrderBovespaSystemInformation.SystemDate.Day.ToString().PadLeft(2, '0') +
                                          'A';

                    if (base.MainRepositoryMessage != null)
                    {
                        _traceWriterSTM.Trace("STMClient_onConnect Limpando a Fila Principal [MainRepositoryMessage( " + base.MainRepositoryMessage.Path + " )] ");
                        base.MainRepositoryMessage.Purge();
                    }
                }

                _traceWriterSTM.Trace("STMClient solicitando mensagens [StartRequest( " + _lastMsgIdDelivered + " )] ");

                _stmAdapter.StartRequest(_lastMsgIdDelivered);

                if (thrMessagesDelivery == null)
                {
                    thrMessagesDelivery = new Thread(new ThreadStart(MessageDelivery));
                    thrMessagesDelivery.Name = "STM Message Delivery";
                    thrMessagesDelivery.Start();

                    MainRepositoryMessage.OnHealthStatusChanged += new Sinacor.OMM.ConnectionManager.MSMQ.MsmqSender.HealthStatusChanged(MainRepositoryMessage_OnHealthStatusChanged);
                }
            }
            catch (Exception ex)
            {
                _traceWriterSTM.Trace("Erro _stmAdapter_OnConnect: " + ex.StackTrace);
                EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
                UnloadStm();
            }
        }

        void _stmAdapter_OnAcceptStart()
        {
            //
        }

        #endregion

        private void TracerHealthMonitor()
        {
            _traceWriterHealthMonitor.Trace(@"STM HealthMonitor: " + System.Environment.NewLine +
                                                                 "[ ReceivedMessagesQuantity: " + _receiverHealthStatus.ReceiverDataInfo.StatisticInfo.lReceivedMessagesQuantity + System.Environment.NewLine +
                                                                 " StatisticInfo.LastReceivedMessageTimeStamp: " + _receiverHealthStatus.ReceiverDataInfo.StatisticInfo.LastReceivedMessageTimeStamp + System.Environment.NewLine +
                                                                 " StatisticInfo.ReceivedMessagesForSecond: " + _receiverHealthStatus.ReceiverDataInfo.StatisticInfo.ReceivedMessagesForSecond + System.Environment.NewLine +
                                                                 " StatisticInfo.LastReceivedMessageId: " + _receiverHealthStatus.ReceiverDataInfo.StatisticInfo.LastReceivedMessageId + System.Environment.NewLine +
                                                                 " StatisticInfo.Connected: " + _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.Connected + System.Environment.NewLine +
                                                                 " StatisticInfo.GoodStateConnected: " + _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.GoodStateConnected + System.Environment.NewLine +
                                                                 " StatisticInfo.TotalInternalQueuedMessages: " + _receiverHealthStatus.ReceiverDataInfo.StatisticInfo.TotalInternalQueuedMessages + System.Environment.NewLine +
                                                                 " StmConnectionInfo.AverageTimeToSendAck: " + _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.AverageTimeToSendAck + System.Environment.NewLine +
                                                                 " StmConnectionInfo.LimitAverageTimeToSendAck: " + _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.LimitAverageTimeToSendAck + System.Environment.NewLine +
                                                                 " StmConnectionInfo.AverageTimeToCallAck: " + _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.AverageTimeToCallAck + System.Environment.NewLine +
                                                                 " StmConnectionInfo.LimitAverageTimeToCallAck: " + _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.LimitAverageTimeToCallAck + System.Environment.NewLine +
                                                                 " StmConnectionInfo.ErrorEventsQuantity: " + _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.ErrorEventsQuantity + System.Environment.NewLine +
                                                                 " StmConnectionInfo.SuccessfulConnectionsQuantity: " + _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.SuccessfulConnectionsQuantity + System.Environment.NewLine +
                                                                 " StmConnectionInfo.UnsuccessfulConnectionsQuantity: " + _receiverHealthStatus.ReceiverDataInfo.StmConnectionInfo.UnsuccessfulConnectionsQuantity + System.Environment.NewLine +
                                                                 " ProcessDataInfo.PrivateMemory: " + _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.PrivateMemory + System.Environment.NewLine +
                                                                 " ProcessDataInfo.HandleCount: " + _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.HandleCount + System.Environment.NewLine +
                                                                 " ProcessDataInfo.ThreadCount: " + _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.ThreadCount + System.Environment.NewLine +
                                                                 " ProcessDataInfo.PrivilegedProcessorTime: " + _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.PrivilegedProcessorTime + System.Environment.NewLine +
                                                                 " ProcessDataInfo.InitializedDate: " + _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.InitializedDate + System.Environment.NewLine +
                                                                 " ProcessDataInfo.ThreadId: " + _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.ThreadId + System.Environment.NewLine +
                                                                 " ProcessDataInfo.ProcessId: " + _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.ProcessId + System.Environment.NewLine +
                                                                 " ProcessDataInfo.MachineMacAddress: " + _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.MachineMacAddress + System.Environment.NewLine +
                                                                 " ProcessDataInfo.MachineName: " + _receiverHealthStatus.ReceiverDataInfo.ProcessDataInfo.MachineName + System.Environment.NewLine +
                                                                 " ReceiverQueue.QueueAcessible: " + _receiverHealthStatus.ReceiverDataInfo.MsmqConnectionInfo.ReceiverQueue.QueueAcessible + System.Environment.NewLine +
                                                                 " ReceiverQueue.QueuedMessages: " + _receiverHealthStatus.ReceiverDataInfo.MsmqConnectionInfo.ReceiverQueue.QueuedMessages + System.Environment.NewLine +
                                                                 " ReceiverQueue.QueuedMessages: " + _receiverHealthStatus.ReceiverDataInfo.MsmqConnectionInfo.ReceiverQueue.MessagesPerSecond + System.Environment.NewLine +
                                                                 " ReceiverQueue.QueuedMessages: " + _receiverHealthStatus.ReceiverDataInfo.MsmqConnectionInfo.ReceiverQueue.QueuePath + System.Environment.NewLine +
                                                                 " ReceiverQueue.QueueBytesSize: " + _receiverHealthStatus.ReceiverDataInfo.MsmqConnectionInfo.ReceiverQueue.QueueBytesSize + " ]", TraceType.Optional);
        }

        protected override bool MessageReceiverArrival(System.Messaging.Message message)
        {
            bool retransmissionOK = false;

            try
            {
                if (message.Body is OMM.MessageEntity.OMMSystem.OMMSystemMessage)
                {
                    _messageReceiveRestart = (OMM.MessageEntity.OMMSystem.OMMSystemMessage)message.Body;

                    switch (_messageReceiveRestart.OMMSystemMessageType)
                    {
                        case Sinacor.OMM.MessageEntity.Enum.OMMSystemMessageType.Restart:
                            RetransmissionRequest(_messageReceiveRestart.SequentialId, _messageReceiveRestart.MessageId);
                            break;
                        case Sinacor.OMM.MessageEntity.Enum.OMMSystemMessageType.DayTradeFinished:
                            UnloadStm();
                            break;
                    }
                }

                retransmissionOK = true;
            }
            catch (Exception ex)
            {
                _traceWriterSTM.Trace("Erro ao receber mensagem do Roteador (MessageReceiverArrival): " + ex.StackTrace);
                EventLog.WriteEntry(LogSources.ReceptorService, string.Concat(ex.Message, " ", ex.StackTrace), EventLogEntryType.Error);
            }

            return retransmissionOK;
        }
    }
}
